home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / gopher / Unix / xgopher.1.3 / Documents / Implementation < prev    next >
Encoding:
Text File  |  1993-05-25  |  22.9 KB  |  654 lines

  1.         Implementation Notes
  2.                on the
  3.             Xgopher client
  4.  
  5.  
  6. These notes describe Xgopher version 1.3, completed in May 1993.
  7. The implementor and author of these notes can be contacted at:
  8.  
  9.     Allan Tuchman
  10.     Computing and Communications Services Office (CCSO)
  11.     University of Illinois at Urbana-Champaign
  12.     1304 W. Springfield Ave.
  13.     Urbana, Illinois   61801
  14.     USA
  15.  
  16.     (217) 244-0048
  17.     a-tuchman@uiuc.edu
  18.  
  19.  
  20. Source Organization
  21. -------------------
  22. The xgopher source files are organized by function.  They are:
  23.  
  24.     xgopher.c - main program, initialize X, set up initial request.
  25.     item.c    - manage the data fields for gopher items
  26.     itemList.c- create and manage the data structures for gopher items
  27.     dir.c     - manage the data fields for gopher directories
  28.     dirList.c - create and manage the data structures for gopher directories
  29.     markList.c- create and manage the bookmark list of gopher items
  30.     util.c    - does gopher processing, get and process gopher requests
  31.     misc.c    - misc things like time functions that may be host dependent
  32.     net.c     - open, read, and write from a network connection
  33.     help.c    - manage help file data.
  34.     bkmkfile.c- manage bookmark file load and save
  35.     itemInfo.c- obtain info about a gopher item or directory
  36.     subst.c   - perform printf-like substitutions of gopher item data
  37.     jobs.c    - keep track of jobs spawned by Xgopher
  38.     sc_*.c    - gopher item class processing for each subclass of gopher item
  39.  
  40.    ------  No file above here has any connection to the X window system -----
  41.  
  42.     gui.c     - interface between the previous files and X functions
  43.     resources.c- retrieve application resources
  44.     cso.c     - create, manage, and process requests for CSO name servers
  45.     index.c   - create and manage panel to get keywords for index searches
  46.     panel.c   - create and manage the main xgopher panel
  47.     error.c   - create and manage the error pop up
  48.     save.c    - create and manage the file save pop up and functions
  49.     options.c - create and manage the pop up for changing run-time options
  50.     single.c  - create and manage the pop up for entering a special item
  51.     text.c    - create and manage the text display pop ups
  52.     version.c - create and manage the version information pop up
  53.     status.c  - create and manage the status popup window
  54.     KeyWSink.c- subclass of AsciiSink widget that can highlight
  55.         selected keywords
  56.  
  57. The include files are used to provide functions definitions, 
  58. configuration options, data structure declarations, share data,
  59. and define bitmaps.  They are:
  60.  
  61.     conf.h       - configuration parameters
  62.     gopher.h     - data structure definitions for items and directories
  63.     resources.h  - the definition of the application resources structure
  64.     appres.h     - the global definition of the app. resources structure
  65.     popres.h     - subresources for popup window positioning
  66.     typeres.h    - subresources for extended type definition
  67.     globals.h    - a couple of variables, symbolic constants, and macros
  68.     oneLine.h    - text widget translations to enable single line edit mode
  69.     osdep.h      - attempt to isolate some OS dependencies for include files.
  70.     MIT_Xosdefs.h- X11R5 OS dependency code from MIT.
  71.     MIT_Xos.h    - X11R5 OS dependency header code from MIT.
  72.     version.h    - Xgopher version info and display text
  73.     xglobals.h   - shared values defined in the X files
  74.     listP.h      - resources used only in {mark,dir)List.c (from resources.c)
  75.     panel.h      - public header file and macros for panel.c
  76.     compatR4.h   - X11R4 compatibility macros
  77.     text.h       - data for text.c
  78.     sc_*P.h      - private header for each subclass of gopher item
  79.     KeyWSink.h   - public header file for KeyWSink widget
  80.     KeyWSinkP.h  - private header file for KeyWSink widget
  81.     bitmaps/*    - contains bitmaps and icons.
  82.  
  83.     The following are all simple function declaration header files:
  84.            item.h, itemList.c, dir.h, dirList.h, markList.h,
  85.            gui.h, misc.h,  net.h,  util.h, help.h,  cso.h, index.h, error.h,
  86.            bkmkfile.h, itemInfo.h, options.h, single.h, jobs.h, status.h,
  87.            subst.h, sc_*.h
  88.  
  89.  
  90. Other files:
  91.  
  92.     Imakefile      - used to generate a Makefile
  93.     Makefile.NoNo  - A LAST RESORT sample Makefile for those without Imake
  94.     README         - documentation file - SHOULD READ BEFORE INSTALLATION
  95.     xgopher.help   - a sample help file
  96.     xgopher.man    - a Unix manual page for xgopher
  97.     Xgopher.ad     - the applications defaults file.  It MUST be used!
  98.     Xgopher-color.ad - applications color resources file.
  99.     Xgopher-complete.ad - combination of the above two files.
  100.     Xgopher-secure.ad - resources file used for public Xgopher kiosk with
  101.                         a touch screen.
  102.     Xgopher-allExtend.ad - Sample app-defaults with lots of extended types.
  103.  
  104. Files in ./Documents:
  105.     Changes        - list of changes since Xgopher 1.2
  106.     Release_Notes  - documentation file - describes changes/features of this
  107.              version of Xgopher.
  108.     Edits          - notes on string editing for command strings
  109.     Extended-Types - detailed notes on use of extended types, with examples
  110.     Install-Customization -
  111.                      Helps the installer customize certain functions
  112.                      of Xgopher, including where to find some external
  113.                      programs that may be useful.
  114.     Popups         - Details on how to use the popup positioning resources
  115.     Porting        - Notes on porting Xgopher to other platforms.
  116.     Implementation - documentation file interesting to the programmer
  117.     Sun-names      - Some details on problems that Sun's may have in
  118.                      resolving host names.
  119.     Sunlib-rebuild - notes on rebuilding a Sun library to help find
  120.                      a name server.
  121.     OW3-site.def   - a configuration file used for compiling with Sun's
  122.              OpenWindows, release 3.
  123.     OW3-xmkmf.patch- an optional fix to Sun's OpenWindows xmkmf program.
  124.  
  125.  
  126. I have tried to keep major operating system dependencies all in the
  127. file misc.c and osdep.h. Different versions of Unix may have slightly
  128. different programming requirements.  These slight variations are handled
  129. through preprocessor directives in the appropriate source files.
  130.  
  131.  
  132. Resource Usage
  133. --------------
  134.  
  135. I have made considerable effort to maintain and reuse every resource
  136. allocated.  Memory for major data structures is never freed back to the
  137. system, but structures that are no longer in use are maintained and reused
  138. again later.  The idea is that it is expensive to get and build
  139. things, but cheap to reuse them.  This philosophy applies to
  140.   . gopher items (the lines you see on the screen and their selector
  141.     information).
  142.   . gopher directories
  143.   . X window text popups
  144.   . Other popups, panels, and help data.
  145.   . Lists created and used to display the X directory and bookmark
  146.     contents lists.
  147.  
  148.  
  149. Positioning Popup Windows
  150. -------------------------
  151.  
  152. Everyone has there own preferences as to where popups should appear.
  153. There are good arguments for having them 1) near the cursor, 2) a
  154. constant position on the main application window, and 3) a constant
  155. location on the screen.
  156.  
  157. If you want to change popup positions, here's how to do it in the
  158. source program.  Future versions of Xgopher may allow such
  159. preferences in the application defaults file.
  160.  
  161. Text (and help) displays are not transient windows, and I leave their
  162. placement to the window manager and the user's window manager preferences.
  163. For all transient windows I place them with a single, pretty general
  164. function call.  The call 
  165.  
  166.     positionPopup(w, from, fromWidget, x, y, hJust, vJust);
  167.  
  168. is used just before poping-up widget w.  If one wants to change
  169. the pop-up location or policy for a specific widget, one need
  170. only change the parameters to this procedure.  The rest or the
  171. parameters are:
  172.  
  173.     from  takes it value from the set of symbolic constants:
  174.       { POS_none, POS_pointer, POS_appPanel, POS_screen }, which
  175.       do respectively,  no positioning, position at the pointer
  176.       location, position relative to an application window, or 
  177.       position relative to the screen.
  178.     
  179.     fromWidget
  180.       For from=POS_appPanel, this is the widget (window) to position on.
  181.     
  182.     x, y  provide a location in percent across the application window
  183.       or screen (depending on from).  For example, x=50, y=50
  184.       represents the center of either the window or the screen.
  185.     
  186.     hJust, vJust
  187.       are horizontal and vertical justification requirements.  For
  188.       hJust = 0 the left edge of the popup is positioned at x;
  189.       hJust = 1 the center of the popup is positioned at x;
  190.       hJust = 2 the right edge of the popup is positioned at x.
  191.       The same is true for vJust, using top, middle, and bottom
  192.       against y.
  193.  
  194.       These justifications do not take into account the size of
  195.       the window manager decorations, but they're close enough
  196.       anyway to be pretty useful.
  197.  
  198. After positioning, positionPopup forces the complete popup to lie
  199. on screen by "shoving" it, if necessary, just enough to get it fully
  200. on the display.
  201.  
  202.  
  203. Hacker's Corner
  204. ---------------
  205.  
  206. Using the "Enter Gopher Item" menu item and selecting a directory
  207. but using type=0 (text file), you can capture a raw gopher directory
  208. response into a text file.  I find this useful for debugging and
  209. for documentation.  You can also set type=9 to copy any file in
  210. binary mode.
  211.  
  212.  
  213. Differences from Previous Versions of Xgopher
  214. ---------------------------------------------
  215.  
  216. The earliest released versions of Xgopher (Xgopher 1.0, 1.1, and 1.1a)
  217. maintained a notion of a dynamic n-ary tree that the user traversed
  218. through gopher space.  Along with this tree, was the notion of a bookmark
  219. being a "place", and internal node in this tree.  The main gopher data
  220. structures were maintained by the routines in the file list.c.
  221.  
  222. For better or worse, the Unix curses client defined peoples expectations 
  223. of bookmark behavior, and indeed, the way gopher space was presented.
  224. As such, the entire basic data structure has been changed at Xgopher 1.2.
  225. The n-ary tree has been replaced with a directory stack.  Most important,
  226. bookmarks are no longer places (directories), but things (items).  Of
  227. course, an item may be a directory.  The file list.c has been replaced
  228. with 5 smaller files (item.c, itemList.c, dir.c, dirList.c, markList.c).
  229.  
  230. Xgopher 1.3 introduced significant changes in the way gopher item
  231. classes were handled internally, giving far better maintenance control.
  232. Extended types make source changes unnecessary for adding many new
  233. gopher types for special purposes, testing, and local customization.
  234.  
  235.  
  236. Subclass Implementation for Gopher Items
  237. ----------------------------------------
  238.  
  239. === This section is totally changed for Xgopher 1.3 ===
  240.  
  241.  
  242. To implement a sort of subclass using standard C, I introduced
  243. to the base class a new field, sc, a pointer to a subclass
  244. structure.  For the base class, this points to a structure of
  245. default (null or unknown) data and methods.
  246.  
  247. I chose this method instead of one like X uses for widget 
  248. subclassing because 1) I want to be able to reuse items with
  249. different subtypes without incurring the expense of freeing
  250. and reallocating them; 2) The subclass records are all of the
  251. same description; and 3) I don't need to go very deep in
  252. subclassing.
  253.  
  254. I also didn't use a function table as would be generated by
  255. C++, again, because I have a simple, constant subclass record
  256. structure, and the type (subclass) of an item will not change
  257. over its useful lifetime.
  258.  
  259. So, scInfo (struct subClassInfoStruct) contains a few constant
  260. strings and function references that will be constant for each
  261. item of that class.
  262.  
  263.  
  264. Adding a New Internal Item Type to Xgopher
  265. ------------------------------------------
  266.  
  267. To add a new class:
  268.  
  269. 1. create the private class record in a header file named
  270.  
  271.     sc_<type>P.h
  272.  
  273.    It should start out something like this:
  274.  
  275.     /* sc_<type>P.h
  276.        gopher item subclass: <type> */
  277.     
  278.     /* ----- subclass record definition ----- */
  279.     
  280.     scInfo    <type>Subclass = {
  281.             "<description of type>",
  282.             prefix<Type>,
  283.             nullHostList,
  284.             checkAccess,
  285.             GI_copyItemToFile,
  286.             copyTypeBinaryEOF,
  287.             GI_noProcess,
  288.             GI<type>Init,
  289.             GI_done,
  290.             GI<type>restart
  291.         };
  292.  
  293.    Also create a public class header with the function definitions.
  294.  
  295.    You can model both of these files on an existing subclass.
  296.  
  297. 2. Add the prefix<Type> resource by modifying
  298.     
  299.     resources.h
  300.     resources.c
  301.     prefixP.h
  302.     conf.h        for the default (PREFIX_<TYPE>)
  303.  
  304.    Follow the examples by looking for "prefixDir" in each file, and do the
  305.    same thing for the new type.
  306.  
  307. 3. Add the prefix<Type> resource to Xgopher.ad (again, look for the area
  308.    around prefixDir and do the same.
  309.  
  310. 4. Modify util.c:
  311.    . Add the new header file with all the other "sc_something.h" includes.
  312.    . Add the new type character and subclass record to the list defined by:
  313.  
  314.     static gopherTypeClass     types[] = {
  315.         :
  316.         :
  317.     };
  318.  
  319.  
  320. ===========================================================================
  321. There now a new type defined in Xgopher, but with only default behavior.
  322. It can only be accessed by a "copy", not a "fetch".  Copy will be in 
  323. binary (not ascii).  The only access limits imposed my be against ftp
  324. (no host access lists).  And there is not special processing upon a
  325. restart command.
  326.  
  327. Time to customize this item.  If it is very similar in behavior to another
  328. gopher type, it is a good idea to look at the subclass methods for that
  329. other type (in the sc_other.[hc] files before proceding.
  330. ===========================================================================
  331.  
  332. 5. For each of the following routines, write new versions if the 
  333.    default behavior is not acceptable.
  334.    . The procedures should be placed in the file sc_<type>.c
  335.    . The procedures should be declared static unless they are
  336.      callbacks or some other unusual circumstance dictates.
  337.    . Function declaration headers for these procedures should be
  338.      included in sc_<type>.h before the subclass record is defined.
  339.    . The names below are suggested names.
  340.  
  341.    A. checkAccess    - suggested name is GI<Type>_checkAccess
  342.       Any checks to limit access to an item by host, domain, ftp, 
  343.       or other criteria.  The default procedure often suffices.
  344.  
  345.    B. copyItemToFile    - suggested name is GI<Type>_copyItem
  346.       Copy processing procedure for this item type when the "copy
  347.       selection to file" button is pressed.  The default procedures
  348.       GI_copyItemToFile or GI_noCopyItem often suffice.
  349.  
  350.    C. process        - suggested name is GI<Type>_process
  351.       The normal processing procedure for this item type when the
  352.       "fetch selection" button is pressed.  Either GI_noProcess is
  353.       useful or a custom procedure is written for this method.
  354.       Process procedures are responsible for keeping the user
  355.       informed about what's going on - the current status of
  356.       their request.  Although not necessary for correct function,
  357.       it is nice to provide some feedback.  The status popup
  358.       shared by other classes may be used.
  359.  
  360.    D. copy        - suggested name is copyBin, copyAscii, or
  361.               GI<Type>_copy
  362.       Normally, one of the two existing procedures copyBin or copyAscii
  363.       is used.
  364.  
  365.    E. restart        - suggested name is GI<Type>_restart
  366.       This is a class-level restart.  It is used for example, to pop
  367.       down any windows associated with a CSO name server, an index
  368.       search query, or text windows.
  369.  
  370.  
  371. Gopher Directory Data Structure
  372. -------------------------------
  373.  
  374. === This section is totally changed as of Xgopher 1.2 ===
  375.  
  376. The gopher directories are maintained in a stack.  The stack is
  377. implemented as modified last-in-first-out (LIFO) data structure.
  378. The modification is that a "pop" operation decrements the top-of-stack
  379. pointer as usual, but the previous top-of-stack item is not yet freed
  380. or removed from the stack.  This occurs when the next "push" operation
  381. occurs.  This allows the stack to be traversed backward, then forward
  382. again (even though the forward traversal is not normal stack behavior).
  383. Strange? Perhaps; but it doesn't cost anything to do it this way, and
  384. it allows one to recover easily from a mistaken "go-back" type of command.
  385. It is also cheaper for the user, since the contents of the directories
  386. are maintained (not refetched) as long as they are maintained in the
  387. stack.
  388.  
  389. The stack, then, may look like this:
  390.  
  391.  firstDir                              currentDir--+
  392.    |                                               |
  393.    |                                               V
  394.    |  +-----+------+           +-----+------+   +-----+------+   +-----+------+
  395.    +->| dir | next+|--> ... -->| dir | next+|-->| dir | next+|-->| dir | NULL |
  396.       |  1  |      |           | N-2 |      |   | N-1 |      |   |  N  |      |
  397.       +-----+------+           +-----+------+   +-----+------+   +-----+------+
  398.  
  399. currentDir normally would point as the last directory in the list.
  400. In the diagram above, the user has selected the "previous directory"
  401. function to return one level back.
  402.  
  403. If the user now selects anything except "move forward" (NOT A FUNCTION
  404. DEFINED BY DEFAULT), then this new selection will replace directory N
  405. and all of its successors.  Each directory from N on is released.
  406.  
  407. Once a directory is released, the directory data structure
  408. is removed from the stack and moved back to a "free list".
  409.  
  410. Gopher items are saved (cached) with the directory until either the
  411. directory is released or they become stale.  A time field in the directory
  412. structure identifies the time that the directory contents were
  413. last fetched.  Whenever the contents are needed, the time is
  414. checked, and if some user-specified threshold of elapsed time
  415. is exceeded, then the directory contents are freed and re-requested
  416. from the server.
  417.  
  418.  
  419.     Directory Node structure (gopherDir)
  420.  
  421.                                    gopherItem
  422.                             .
  423.                            / \
  424.                             |
  425.                             |
  426.                +----------+--------------+------+
  427.       gopherDir <--|+previous | selectorItem | next+|----> gopherDir
  428.                +----------+--------------+------+
  429.                | created  | contents            |
  430.                +----------+---------------------+
  431.                                     |
  432.                                     |
  433.                            \ /                
  434.                             .
  435.                                  gopherItemList
  436.  
  437.  
  438. A gopherItemList is simply a first/last pointer structure:
  439.  
  440.       +--------+
  441.       | first +|---> gopherItem
  442.       +--------+
  443.       | last  +|---> gopherItem
  444.       +--------+
  445.  
  446. And the gopherItem structure is:
  447.  
  448.       +------+------+------+------+
  449.       | type | name | path | next+|--->
  450.       +------+------+------+------+
  451.       | host | port | plus        |
  452.       +------+------+-------------+
  453.  
  454.  
  455. Button Sensitivity
  456. ------------------
  457.  
  458. The buttons are set sensitive according to the following conditions
  459.  
  460.  . Fetch:    if ANYTHING selected
  461.  . Copy:    if ANYTHING selected
  462.  . Previous:    if previousDir != NULL
  463.  . RemoveMark:    if ANY MARK selected
  464.  . RemoveAllMarks:    if markListLen != 0
  465.  . LoadMarks:    if BookmarkFile != NULL and != empty string
  466.  . SaveMarks:    if BookmarkFile != NULL and != empty string and
  467.                    allowBookmarkSave and markListLen != 0
  468.  . Info:    if ANYTHING selected ==> label = "info on item"
  469.                 else if currentDir exists ==> label = "info on directory"
  470.  . Mark:    if ANY NON-MARK selected ==> label = "mark item"
  471.                 else if currentDir exists ==> label = "mark directory"
  472.  
  473.  
  474. Widget Tree
  475. -----------
  476.  
  477. If you want to better customize some of the widgets or perhaps modify
  478. Xgopher, a widget tree is helpful.  This one isn't beautiful, but
  479. it was up to date as of the release of Xgopher 1.3.
  480. Each line contains first the widget class, then the instance name.
  481. Indentation shows the parent-child relationships.
  482.  
  483. Xgopher  xgopher
  484.     Paned  gopherPanel
  485.         Form  statusForm
  486.             Command  quit
  487.             MenuButton  other
  488.                 SimpleMenu  otherActionsMenu
  489.                     SmeBSB  copy
  490.                     SmeBSB  unmarkAll
  491.                     SmeBSB  loadMarks
  492.                     SmeBSB  saveMarks
  493.                     SmeBSB  options
  494.                     SmeBSB  oneItem
  495.                     SmeBSB  version
  496.                     SmeBSB  restart
  497.             Command  help
  498.             Label  status
  499.         Form  directoryForm
  500.             Label  directoryTitle
  501.             Viewport  directoryView
  502.                 Core  clip
  503.                 Scrollbar  vertical
  504.                 List  directory
  505.         Box  goBox
  506.             Command  fetch
  507.             Command  info
  508.             Command  up
  509.             Simple  spacer
  510.             Command  mark
  511.             Command  unmark
  512.         Form  bookmarkForm
  513.             Label  bookmarkTitle
  514.             Viewport  bookmarkView
  515.                 Core  clip
  516.                 Scrollbar  vertical
  517.                 List  bookmark
  518.         Grip  grip
  519.         Grip  grip
  520.     TransientShell  errorDialogShell
  521.         Dialog  errorDialog
  522.             Label  icon
  523.             Label  label
  524.             Command  ok
  525.     TransientShell  optionsPanel
  526.         Form  optionsForm
  527.             Box  buttonBox
  528.                 Command  done
  529.                 Command  cancel
  530.                 Command  help
  531.             Form  itemsForm
  532.                 Box  showWhatBox
  533.                     Toggle  showWhat
  534.                     Label  showWhatLabel
  535.                 Box  appendBkBox
  536.                     Toggle  appendBk
  537.                     Label  appendBkLabel
  538.                 Box  loadBkBox
  539.                     Toggle  loadBk
  540.                     Label  loadBkLabel
  541.                 Box  resetBox
  542.                     Toggle  reset
  543.                     Label  resetLabel
  544.                 Form  bkSaveForm
  545.                     Label  bkSaveLabel
  546.                     Text  bkSave
  547.                 Form  printCmdForm
  548.                     Label  printCmdLabel
  549.                     Text  printCmd
  550.                 Form  imageCmdForm
  551.                     Label  imageCmdLabel
  552.                     Text  imageCmd
  553.                 Form  telCmdForm
  554.                     Label  telCmdLabel
  555.                     Text  telCmd
  556.                 Form  t3270CmdForm
  557.                     Label  t3270CmdLabel
  558.                     Text  t3270Cmd
  559.     TransientShell  singlePanel
  560.         Form  singleForm
  561.             Box  buttonBox
  562.                 Command  done
  563.                 Command  cancel
  564.                 Command  clear
  565.                 Command  mark
  566.                 Command  help
  567.             Form  itemsForm
  568.                 Form  giTypeForm
  569.                     Label  giTypeLabel
  570.                     Text  giType
  571.                 Form  giNameForm
  572.                     Label  giNameLabel
  573.                     Text  giName
  574.                 Form  giPathForm
  575.                     Label  giPathLabel
  576.                     Text  giPath
  577.                 Form  giHostForm
  578.                     Label  giHostLabel
  579.                     Text  giHost
  580.                 Form  giPortForm
  581.                     Label  giPortLabel
  582.                     Text  giPort
  583.     TransientShell  infoDialogShell
  584.         Dialog  infoDialog
  585.             Label  icon
  586.             Label  label
  587.             Command  ok
  588.     TopLevelShell  csoShell
  589.         Form  csoForm
  590.             Label  nameServer
  591.             Command  csoDone
  592.             SimpleMenu  csoFieldsMenu
  593.                 SmeBSB  Default
  594.                 SmeBSB  Lookup
  595.                 SmeBSB  Indexed
  596.                 SmeBSB  Public
  597.             MenuButton  csoFields
  598.             Command  csoHelp
  599.             Label  csoQueryLabel
  600.             Text  csoQueryText
  601.             Command  csoDoQuery
  602.             Command  csoClearQuery
  603.             Command  csoClearText
  604.             Text  csoText
  605.     TransientShell  indexShell
  606.         Form  indexForm
  607.             Command  indexDoIndex
  608.             Command  indexCancel
  609.             Command  indexHelp
  610.             Label  indexLabel
  611.             Text  indexQueryText
  612.     TransientShell  saveShell
  613.         Form  saveForm
  614.             Label  dirLabel
  615.             Text  dirPath
  616.             Label  saveLabel1
  617.             Label  saveLabel2
  618.             Text  fileName
  619.             Label  fileErrMessage
  620.             Command  ok
  621.             Command  cancel
  622.             Command  chdir
  623.             Command  help
  624.         TransientShell  cdShell
  625.             Form  cdForm
  626.                 Label  cdLabel
  627.                 Text  cdPathName
  628.                 Label  cdErrMessage
  629.                 Command  ok
  630.                 Command  cancel
  631.                 Command  help
  632.         TransientShell  dupFileShell
  633.             Form  dupFileForm
  634.                 Label  label1
  635.                 Label  labelFile
  636.                 Label  label2
  637.                 Command  ok
  638.                 Command  cancel
  639.                 Command  append
  640.                 Command  change
  641.                 Command  help
  642.  
  643.  
  644. Special thanks
  645. ------------------
  646.  
  647. I want to thank everyone who took the time to send me comments, suggestions,
  648. and constructive criticism on previous versions of Xgopher.
  649. Much of the Xgopher 1.3 change was motivated by this feedback.
  650. Also, thanks to the several people who helped beta test this release.
  651.  
  652. Of course, the present author assumes responsibility for any
  653. problems introduced.
  654.